/**
 * Copyright 2000 self.com.au
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. (http://www.apache.org/licenses/LICENSE-2.0)
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for 
 * the specific language governing permissions and limitations under the License.
 *
 * <dhamma-message src="Atisha (11th century Tibetan Buddhist master)">
 * 	The greatest achievement is selflessness. The greatest worth is self-mastery.
 * 	The greatest quality is seeking to serve others. The greatest precept is continual awareness.
 * 	The greatest medicine is the emptiness of everything. The greatest action is not conforming with the worlds ways.
 * 	The greatest magic is transmuting the passions. The greatest generosity is non-attachment.
 * 	The greatest goodness is a peaceful mind. The greatest patience is humility.
 * 	The greatest effort is not concerned with results. The greatest meditation is a mind that lets go.
 * 	The greatest wisdom is seeing through appearances. 
 * </dhamma-message>
 *
 * @author Ashin Wimalajeewa
 */
package self.amigo.elem;

import java.awt.*;
import java.awt.event.*;
import java.util.*;

import org.w3c.dom.*;

import self.gee.*;
import self.lang.*;
import self.xml.*;

public class BasicLinkView extends PolyLineView implements ILinkGraphic {
	
	/**
	 * If the internal state of this class ever changes in such a way that it can't be defaulted,
	 * then the {@link #serialVersionUID} should be incremented to ensure serialized instances cleanly fail.  
	 */
	private static final long serialVersionUID = 1;
	
  final static public String LINK_PROP = "link";
  
  protected ILinkableGraphic sideA, sideB;
  
  public void linkerHasChanged( ILinkableGraphic mover, int event ) {
    if ( ILinkableGraphic.REMOVED_EVENT == event ) 
      remove();
    else {
      updateLinkerIntersections();
    }
  }
  
  public void refreshEndPoints() {
    updateLinkerIntersections();
  }
  
  public ILinkableGraphic getSideA() {
    return sideA;
  }
  
  public ILinkableGraphic getSideB() {
    return sideB;
  }
  
  public void linkSideA( ILinkableGraphic elem ) {
    if ( sideA != null )
      detachToSide( sideA );
    sideA = elem;
    attachToSide( sideA );

    Point from = (Point) points.get( 1 );
    int[] intersect = new int[2];
    sideA.getLineIntersectionToCentre( from.x, from.y, intersect );
    setLocation( intersect[0], intersect[1] );
  }

  public void linkSideB( ILinkableGraphic elem ) {
    if ( sideB != null )
      detachToSide( sideB );
    sideB = elem;
    attachToSide( sideB );

    int max = points.size();
    Point from = (Point) points.get( max-2 );
    int[] intersect = new int[2];
    sideB.getLineIntersectionToCentre( from.x, from.y, intersect );
    setLastLocation( intersect[0], intersect[1] );
  }
  
  public void remove() {
    super.remove();
    if ( sideA != null )
      detachToSide( sideA );
    if ( sideB != null )
      detachToSide( sideB );
  }
  
  public void       move( Dimension moveBy, MouseEvent e, int dragState ) {
    super.move( moveBy, e, dragState );
    updateLinkerIntersections();  
  }
  
  public void       resize( int x, int y, Point[] handle, MouseEvent e, int dragState ) {
    super.resize( x, y, handle, e, dragState ); 
    boolean updateLinks = true;
    if ( isPointSideA(handle[0]) ) {
      if ( dragState == IGraphicElement.DS_AFTER_LAST ) {
        setNewLinkTarget( handle[0], true );
      } else
        updateLinks = false;
    } else if ( isPointSideB(handle[0]) ) {
      if ( dragState == IGraphicElement.DS_AFTER_LAST ) {
        setNewLinkTarget( handle[0], false );
      } else
        updateLinks = false;
    } 
    if ( updateLinks )
      updateLinkerIntersections();
  }
  
  public boolean    areDependentsListed( java.util.List toCheck ) {
    boolean ok = false;
    if ( sideA != null ) {
      if ( toCheck.contains(sideA) )
        ok = sideA.areDependentsListed( toCheck );
    }
    if ( ok ) {
      if ( sideB != null ) {
        if ( toCheck.contains(sideB) )
          ok = sideB.areDependentsListed( toCheck );
      }
    }
    return ok;
  }
  
  public boolean isPointSideA( Point toTest ) {
    Point firstPoint = (Point) points.get( 0 );
    return ( toTest == firstPoint );
  }

  public boolean isPointSideB( Point toTest ) {
    Point lastPoint = (Point) points.get( points.size()-1 );
    return ( toTest == lastPoint );
  }
  
  
  public String toString() {
    String sAhc = sideA == null ? "null": "" + sideA.hashCode();
    String sBhc = sideB == null ? "null": "" + sideB.hashCode();
    String ret = "[" + this.getClass().getName() + "@" + hashCode() +
                  "sideA=" + sAhc + ", sideB=" + sBhc +
                "]" + super.toString();
    return ret;                
  }
  
  // IPersistentElement support 
  public void readFrom( Node self, HashMap idObjLookUp ) throws DOMException {
    super.readFrom( self, idObjLookUp );

    Node sA = DomUtils.getChildElementNode( self, PolyLineView.SIDEA_PROP );
    Node sB = DomUtils.getChildElementNode( self, PolyLineView.SIDEB_PROP );
    
    String idA = DomUtils.getElementAttribute( sA, LINK_PROP );
    String idB = DomUtils.getElementAttribute( sB, LINK_PROP );
    sideA = (ILinkableGraphic) idObjLookUp.get( idA );
    sideB = (ILinkableGraphic) idObjLookUp.get( idB );
  }

  public void writeTo( Document doc, Element self, HashMap objIdLookup ) throws DOMException {
    String idA = (String) objIdLookup.get( sideA );
    String idB = (String) objIdLookup.get( sideB );
    if ( (idA == null) || (idB == null) ) {
      self.getParentNode().removeChild( self );
      return;
    }
    super.writeTo( doc, self, objIdLookup );
    Element sA = (Element) DomUtils.getChildElementNode( self, PolyLineView.SIDEA_PROP );
    DomUtils.setElementAttribute( sA, LINK_PROP, StringUtils.toEmptyIfNull(idA) );
    Element sB = (Element) DomUtils.getChildElementNode( self, PolyLineView.SIDEB_PROP );
    DomUtils.setElementAttribute( sB, LINK_PROP, StringUtils.toEmptyIfNull(idB) );
  }
  
  protected int getMinEatHandles() {
    return sideA == sideB? 3: 2;
  }
  
  protected IGraphicElement getElementAtPoint( Point handle ) {
    return layer.getElementAtPoint( handle, this );
  }
  
  protected void attachToSide( ILinkableGraphic side ) {
    side.attachElement( this );
  }

  protected void detachToSide( ILinkableGraphic side ) {
    side.detachElement( this );
  }
  
  private void setNewLinkTarget( Point handle, boolean isA ) {
//    IGraphicElement elem = layer.getElementAtPoint( handle, this );
    IGraphicElement elem = getElementAtPoint( handle );
    if ( elem != null ) {
      if ( elem == this )
        return;
      if ( elem instanceof ILinkableGraphic ) {
        ILinkableGraphic linker = (ILinkableGraphic) elem;
        ILinkableGraphic otherSide  = isA ? sideB: sideA;
        if ( (linker == otherSide) && (getNoOfPoints() == 2) ) {
        	// nothing to do.
        } else {
          if ( isA )
            linkSideA( linker );
          else
            linkSideB( linker );
          layer.setDirty();
        }
      }
    }
  }
  
  private void updateLinkerIntersections() {
    Point from;
    int[] intersect = new int[2];

    // sideA
    from = (Point) points.get( 1 );
    sideA.getLineIntersectionToCentre( from.x, from.y, intersect );
    setLocation( intersect[0], intersect[1] );

    // sideB
    int max = points.size();
    from = (Point) points.get( max-2 );
    sideB.getLineIntersectionToCentre( from.x, from.y, intersect );
    setLastLocation( intersect[0], intersect[1] );
  }
}
